﻿using Delphi.Apoll.Updater;
using Delphi.Apoll.Util;
using Delphi.Isis.EodServiceReference;
using Delphi.Isis.ServiceClients;
using Delphi.Janus.Util;
using Delphi.Mimir.Data.Bulk;
using Delphi.Mimir.Data.Entities.Final;
using Delphi.Mimir.Data.Entities.Final.Helpers;
using Delphi.Mimir.Data.Procedures;
using Delphi.Mimir.Wrapper;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Delphi.Apoll.Eod {
    public class EodUpdater : IJob, IDisposable, IUpdater {
        private Parameter _credentials;
        private EodServiceClient _eod;
        private BulkLoad _bulk;
        public EodUpdater() {
            _credentials = ParameterWrapper.GetParameter("EOD", "Mimir", "Login", "WebService");
            _eod = EodServiceClient.GetInstance(_credentials.OutParm1, _credentials.OutParm2);
            _bulk = BulkLoad.GetInstance();
        }

        public void UpdateAll() {

            // due to performance, coa api must only be called once and only when necessary (querry runs for ~10 minutes)
            bool coaUpdateFlag = false;

            ICollection<ExchangeLastLoad> ell = ExchangeWrapper.GetLastLoads();
            YahooUpdater yup = new YahooUpdater();

            foreach (ExchangeLastLoad el in ell) {

                ICollection<ExchangeLastLoad.LastLoadControls> llc = el.LastControls.ToList();
                foreach (ExchangeLastLoad.LastLoadControls lc in llc) {
                    // Last trade date of loaded data
                    DateTime lastLoadedTradeDate = new DateTime();
                    // DateTime when the next trade date should be loaded --> lastLoaderdTradeDate + 1 Bus Day + Offsets
                    DateTime nextLoadDue = new DateTime();
                    // whenever a temp date comes in handy.....
                    DateTime tempDate = new DateTime();

                    bool firstLoad = false;

                    // Handle null date
                    if (lc.LastLoad == null) {
                        lastLoadedTradeDate = DateTime.UtcNow.NextBusinessDays(-7);
                        firstLoad = true;
                    } else {
                        lastLoadedTradeDate = lc.LastLoad.TradeDate;
                    }

                    Console.Write(string.Format("Loading {0} for {1}... ", lc.LoadTypeCode, el.ExchangeCode));

                    switch (lc.LoadTypeCode) {
                    //switch (el.ExchangeCode) {
                        case "SYM":
                            // only current date available so only load data once when its due
                            tempDate = lastLoadedTradeDate.Date.NextBusinessDay();
                            nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);
                            if (nextLoadDue < DateTime.UtcNow & lastLoadedTradeDate < DateTime.UtcNow.Date) {
                                Symbols(el.ExchangeCode, lc.LoadControlId);
                            }
                            break;

                        case "QOTEOD":
                            // get end-of-day quotes from last load date until today
                            tempDate = lastLoadedTradeDate.Date.NextBusinessDay();
                            nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);

                            bool getHistory = true;
                            int nofHistDays = 3;

                            while(nextLoadDue < DateTime.UtcNow & lastLoadedTradeDate < DateTime.UtcNow.Date) {

                                // Due to Data Quality issues: If data is loaded, get "yesterday" again....
                                
                                if(getHistory && ((!el.ExchangeCode.Equals("OPRA") && !el.ExchangeCode.Equals("USMF")))) {

                                    //BusinessDay.Next()
                                    Quotes(el.ExchangeCode, tempDate.NextBusinessDays(nofHistDays * -1), tempDate.PreviousBusinessDay(), "D");
                                    getHistory = true;
                                }
                                Quotes(el.ExchangeCode, tempDate.Date, "D", lc.LoadControlId);
                                tempDate = tempDate.NextBusinessDay().Date;
                                nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);
                            }
                            break;

                        case "QOTINTRA":
                            // Make sure intra day is available
                            if(el.HasIntraDayProduct == true) {
                                // get eond-of-day quotes from last load date until today
                                tempDate = lastLoadedTradeDate.NextBusinessDay().Date;
                                nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);

                                while (nextLoadDue < DateTime.UtcNow & lastLoadedTradeDate < DateTime.UtcNow.Date) {
                                    Quotes(el.ExchangeCode, tempDate.Date, "1", lc.LoadControlId);
                                    tempDate = tempDate.NextBusinessDay().Date;
                                    nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);
                                }
                            }
                            break;

                        case "COA":
                            if(firstLoad) {
                                tempDate = lastLoadedTradeDate.Date;
                            } else {
                                tempDate = lastLoadedTradeDate.NextBusinessDays(2).Date;
                            }

                            nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);
                            if (nextLoadDue < DateTime.UtcNow & lastLoadedTradeDate < DateTime.UtcNow.Date & (DateTime.UtcNow.DayOfWeek == DayOfWeek.Saturday | DateTime.UtcNow.DayOfWeek == DayOfWeek.Sunday)) {

                                //YahooUpdater yup = new YahooUpdater();
                                int? rec = yup.UpdateCorporateAction(el.ExchangeCode, lastLoadedTradeDate.AddDays(-14));
                                if (rec == null) {
                                    LoadControlWrapper.AddNewLoad(lc.LoadControlId, DateTime.UtcNow, 0, "Error");
                                } else {
                                    // coa api to be called only when records are loaded
                                    if(rec.Value > 0) {
                                        coaUpdateFlag = true;
                                    }
                                    LoadControlWrapper.AddNewLoad(lc.LoadControlId, DateTime.UtcNow, rec.Value, "Success");
                                }
                            }
                            break;

                        case "FUN":
                            // only current date available so only load data once when its due
                            if(firstLoad) {
                                tempDate = lastLoadedTradeDate.Date;
                            } else {
                                tempDate = new DateTime(lastLoadedTradeDate.Year, lastLoadedTradeDate.Month, 1).AddMonths(1);
                            }

                            nextLoadDue = tempDate.Date.Add(el.ClosingHourUtc).Add(lc.loadOffset);
                            if (nextLoadDue < DateTime.UtcNow & lastLoadedTradeDate < DateTime.UtcNow.Date) {
                                Fundamentals(el.ExchangeCode, lc.LoadControlId);
                            }
                            break;
                    }
                    Console.WriteLine(" Done!");
                }
            }

            // Get Yahoo stuff fo new symbols
            yup.UpdateCoporateActionNewSymbol();
            yup.LoadFullHistoryNewSymbols();

            // call coa api
            if (coaUpdateFlag) {
                Console.Write("Updating Corporate Actions....");
                //Etl.EodCorporateActionsApi();
                //Etl.ClearTable("raw.eod_corporate_actions");
                Console.WriteLine(" Done!");
            }
        }

        public void Fundamentals(string exchangeCode, int loadControlId) {
            // make sure the tables are empty
            Etl.ClearTable("raw.eod_fundamentals");
            int rec = 0;
            string status = "";

            try {
                rec = UpdateFundamentals(exchangeCode);
                if (rec > 0) {
                    status = "Success";
                    // Rund the ETL procedure
                    Etl.EodFundamentalsApi();
                } else {
                    // No error but webservice delivered no data...
                    status = "No Data";
                }
            } catch (Exception e) {
                // Oopsie, something went wrong here
                status = "Error";
                Log.Write(string.Format("Updater.Fundamentals({0}, {1})", exchangeCode, loadControlId), e);
                Log.Write(e.StackTrace);
            }
            // Rund the ETL procedure
            Etl.EodFundamentalsApi();
            LoadControlWrapper.AddNewLoad(loadControlId, DateTime.UtcNow, rec, status);

            // and clean-up
            Etl.ClearTable("raw.eod_fundamentals");
        }

        public void Symbols(string exchangeCode, int loadControlId) {
            // make sure the tables are empty
            Etl.ClearTable("raw.eod_symbols");
            //Etl.ClearTable("raw.eod_symbol_changes");

            // Get data from webservice
            int rec = 0;
            string status = "";
            
            try {
                rec = UpdateSymbols(exchangeCode);
                UpdateSymbolChanges(exchangeCode);
                if (rec > 0) {
                    status = "Success";
                    // Rund the ETL procedure
                    Etl.EodSymbolsApi();
                } else {
                    // No error but webservice delivered no data...
                    status = "No Data";
                }
            } catch (Exception e) {
                // Oopsie, something went wrong here
                status = "Error";
                Log.Write(string.Format("Updater.Symbols({0}, {1})", exchangeCode, loadControlId), e);
                Log.Write(e.StackTrace);
            }
            // Rund the ETL procedure
            Etl.EodSymbolsApi();
            LoadControlWrapper.AddNewLoad(loadControlId, DateTime.UtcNow, rec, status);

            // and clean-up
            Etl.ClearTable("raw.eod_symbols");
            //Etl.ClearTable("raw.eod_symbol_changes");
        }

        public void Quotes(string exchangeCode, DateTime tradeDate, string period, int loadControlId) {
            // make sure the tables are empty
            Etl.ClearTable("raw.eod_quotes");

            string status = "";
            int rec = 0;
            
            try {
                // Get data from webservice
                rec = UpdateQuotes(exchangeCode, tradeDate, period);
                UpdateSymbolSplits(exchangeCode);
                
                if (rec > 0) {
                    status = "Success";
                    // Rund the ETL procedure
                    Etl.EodQuotesApi();
                } else {
                    // No error but webservice delivered no data...
                    status = "No Data";
                }
            } catch (Exception e) {
                // Oopsie, something went wrong here
                status = "Error";
                Log.Write(string.Format("Updater.Quotes({0}, {1})", exchangeCode, loadControlId), e);
                Log.Write(e.StackTrace);
            }
            LoadControlWrapper.AddNewLoad(loadControlId, tradeDate, rec, status);

            // and clean-up
            Etl.ClearTable("raw.eod_quotes");
        }

        public void Quotes(string exchangeCode, DateTime tradeDate, string period) {
            // make sure the tables are empty
            Etl.ClearTable("raw.eod_quotes");

            string status = "";
            int rec = 0;

            try {
                // Get data from webservice
                rec = UpdateQuotes(exchangeCode, tradeDate, period);
                UpdateSymbolSplits(exchangeCode);

                if (rec > 0) {
                    status = "Success";
                    // Rund the ETL procedure
                    Etl.EodQuotesApi();
                } else {
                    // No error but webservice delivered no data...
                    status = "No Data";
                }
            } catch (Exception e) {
                // Oopsie, something went wrong here
                status = "Error";
                Log.Write(string.Format("Updater.Fundamentals({0})", exchangeCode), e);
                Log.Write(e.StackTrace);
            }
            //LoadControlWrapper.AddNewLoad(loadControlId, tradeDate, rec, status);

            // and clean-up
            Etl.ClearTable("raw.eod_quotes");
        }

        public void Quotes(string exchangeCode, DateTime startDate, DateTime endDate, string period) {
            // make sure the tables are empty
            Etl.ClearTable("raw.eod_quotes");

            string status = "";
            int rec = 0;

            foreach (var day in startDate.EachBusinessDay(endDate)) {
                try {
                    // Get data from webservice
                    rec = UpdateQuotes(exchangeCode, day, period);
                    UpdateSymbolSplits(exchangeCode);

                    if (rec > 0) {
                        status = "Success";
                        // Rund the ETL procedure
                        Etl.EodQuotesApi();
                    } else {
                        // No error but webservice delivered no data...
                        status = "No Data";
                    }
                } catch (Exception e) {
                    // Oopsie, something went wrong here
                    status = "Error";
                    Log.Write(string.Format("Updater.Fundamentals({0})", exchangeCode), e);
                    Log.Write(e.StackTrace);
                }
                // and clean-up
                Etl.ClearTable("raw.eod_quotes");
            }

            
            //LoadControlWrapper.AddNewLoad(loadControlId, tradeDate, rec, status);

            
        }

        private int UpdateQuotes(string exchangeCode, DateTime tradeDate, string period) {
            QUOTE[] quotes = _eod.Quotes(exchangeCode, tradeDate, period);
            _bulk.Persist(quotes, exchangeCode, period);
            return quotes.Length;
        }

        private void UpdateSymbolChanges(string exchangeCode) {
            SYMBOLCHANGE[] changes = _eod.SymbolChanges(exchangeCode);
            _bulk.Persist(changes);
        }

        private void UpdateSymbolSplits(string exchangeCode) {
            SPLIT[] splits = _eod.SymbolSplits(exchangeCode);
            _bulk.Persist(splits);
        }

        private int UpdateSymbols(string exchangeCode) {
            SYMBOL[] syms = _eod.Symbols(exchangeCode);
            _bulk.Persist(syms, exchangeCode);
            return syms.Length;
        }

        private int UpdateFundamentals(string exchangeCode) {
            FUNDAMENTAL[] funs = _eod.Fundamentals(exchangeCode);
            _bulk.Persist(funs, exchangeCode);
            return funs.Length;
        }

        public void Execute(IJobExecutionContext context) {
            UpdateAll();
        }

        public void Dispose() {
            _credentials = null;
            _eod = null;
            _bulk = null;
        }

        private bool WeekEndUpdate(DateTime lastLoadDate) {

            return false;
        }
    }
}
